home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
Graphics
/
Icons
/
next-icon@gun.com
/
Apps
/
ImagePortfolio
/
PaletteMatrix.cxx
< prev
next >
Wrap
Text File
|
1993-06-03
|
23KB
|
917 lines
// -------------------------------------------------------------------------------------
// PaletteMatrix.m
// -------------------------------------------------------------------------------------
// Permission is granted to freely redistribute this source code, and to use fragments
// of this code in your own applications if you find them to be useful. This class,
// along with the source code, come with no warranty of any kind, and the user assumes
// all responsibility for its use.
// -------------------------------------------------------------------------------------
extern "Objective-C" {
#import <objc/objc.h>
#import <appkit/appkit.h>
#import <libc.h>
#import <stdlib.h>
#import <stdio.h>
#import <string.h>
#import <mach/mach.h>
#import <dpsclient/wraps.h>
#import <math.h>
}
#import "ImagePortfolio.h"
#import "ExpandImage.h"
#import "Portfolio.h"
#import "PaletteCell.h"
#import "PaletteMatrix.h"
// -------------------------------------------------------------------------------------
// misc constants/macros
#define prefVERSION 1.02
#define CELLCLASS [PaletteCell class]
#define newCELL [protoCell copy]
#define VSPACER 2.0
#define freeLIST(L) { if (L) { [[L freeObjects] free]; L = (id)nil; } }
#define dbgMARKER(X) { printf("%s; %d...\n", sel_getName(_cmd), X); }
#define cpNIL (char*)nil
// -------------------------------------------------------------------------------------
// origin/size shortcuts
#define X origin.x
#define Y origin.y
#define W size.width
#define H size.height
// -------------------------------------------------------------------------------------
static id paletteCellClass = (id)nil;
static char *cutListPaths = cpNIL;
static int sigPasteboard = -1;
static char *imagePathType = cpNIL;
static id pasteFont = (id)nil;
// -------------------------------------------------------------------------------------
// static selectors
extern "Objective-C" {
static SEL compareCellTitle = @selector(compareCellTitle::);
static SEL _updateCells = @selector(_updateCells);
}
// -------------------------------------------------------------------------------------
@implementation PaletteMatrix
// -------------------------------------------------------------------------------------
/* new instance */
+ newFrame:(const NXRect *)fRect
{
return [[self allocFromZone:NXDefaultMallocZone()] initFrame:fRect];
}
/* initialize instance */
- initFrame:(const NXRect*)fRect
{
/* init globals */
if (!imagePathType) imagePathType = NXCopyStringBuffer("imagePathType");
/* init super */
[super initFrame:fRect];
if (!paletteCellClass) paletteCellClass = CELLCLASS;
/* init vars */
delegate = self;
pbCopyTiff = YES;
pbCopyPath = NO;
loadingCells = NO;
loadMutex = mutex_alloc();
/* image list container */
imageList = [[[List alloc] initCount:1] empty];
deleteList = (id)nil;
/* default protocell */
protoCell = [paletteCellClass new];
intercell.width = VSPACER;
intercell.height = VSPACER;
/* size */
[self setMode:NX_LISTMODE];
[self setEmptySelectionEnabled:YES];
[self setAutoscroll:YES];
[self setBackgroundGray:NX_LTGRAY];
[self setCellBackgroundGray:NX_LTGRAY];
[self setFlipped:YES];
return self;
}
/* free object */
- free
{
[imageList free]; // cells are freed by super
freeLIST(deleteList);
mutex_free(loadMutex);
return [super free];
}
/* free all cells */
- freeCells
{
[self clearSelectedCell];
[self renewRows:0 cols:0];
[cellList freeObjects];
[imageList empty];
[self sizeToCells];
freeLIST(deleteList);
[window setDocEdited:YES];
return self;
}
/* remove selected cells (return deleted list) */
- freeSelectedCells
{
int i;
id iCell, dList = [self selectedCellList];
/* return if no selected cells */
if (!dList) return (id)nil;
/* delete selected cells from matrix */
i = [dList count];
[self clearSelectedCell];
[self renewRows:0 cols:0];
mutex_lock(loadMutex);
while(i) {
iCell = [dList objectAt:--i];
[imageList removeObject:iCell];
[cellList removeObject:iCell];
[iCell setDelegate:(id)nil];
}
mutex_unlock(loadMutex);
[self sizeToCells];
[window setDocEdited:YES];
return dList;
}
// -------------------------------------------------------------------------------------
// set attributes
/* set delegate */
- setDelegate:anObject
{
delegate = anObject;
return self;
}
/* set to font */
- setFont:fontObj
{
int i = [self cellCount];
[super setFont:fontObj];
[window disableDisplay];
while(i) [[cellList objectAt:--i] setFont:fontObj];
[protoCell setFont:fontObj];
conFlags.calcSize = YES;
[window reenableDisplay];
return self;
}
- changeFont:sender
{
[self setFont:[[FontManager new] convertFont:[self font]]];
return self;
}
/* set preferences (THIS ASSUMES PROPER PREFERENCE BUFFER FORMAT) */
- (BOOL)setPreferences:(char*)prefBuff returnRows:(int*)rows cols:(int*)cols
{
NXSize size;
float ver, pointSize;
char fontName[256];
id o;
if (!prefBuff || !*prefBuff) return NO;
sscanf(prefBuff, "%f %f %f %d %d %s %f", &ver,
&size.width, &size.height, cols, rows, fontName, &pointSize);
[self setCellSize:&size];
if (o = [Font newFont:fontName size:pointSize]) [self setFont:o];
return YES;
}
// -------------------------------------------------------------------------------------
// pasteboard
- (char*)imagePaths:list
{
int i, c, len = 0;
char *fullPath, *p;
if (!(c = [list count])) return cpNIL;
for (i=0;i<c;i++) { len += strlen([[list objectAt:i] imagePath]) + 1; }
fullPath = p = (char*)malloc(len + 1);
for (i=0;i<c;i++) { sprintf(p, "%s\t", [[list objectAt:i] imagePath]); p += strlen(p); }
*(p - 1) = 0; // remove trailing tab (p > fullPath is guaranteed)
return fullPath;
}
/* copy listed cells to pasteboard */
- copyToPasteboard:list paths:(char*)listPaths
{
char *dt[2], *data, *path;
int len, max, n = 0;
NXStream *stream;
NXImage *image;
Pasteboard *pb;
id first;
/* return if no items to copy */
if (![list count]) return self;
/* return if no selected cell */
pb = [Pasteboard newName:NXGeneralPboard];
if (!pb) { NXLogError("ImagePortfolio: no pasteboard"); return self; }
/* declare pasteboard types */
if (pbCopyPath) dt[n++] = (char*)NXAsciiPboardType;
if (pbCopyTiff) dt[n++] = (char*)NXTIFFPboardType;
dt[n++] = (char*)imagePathType;
[pb declareTypes:dt num:n owner:NXApp];
sigPasteboard = [pb changeCount];
/* get first image */
first = [list objectAt:0];
/* write image representation to pasteboard */
if (pbCopyTiff) {
stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
image = [first image];
[image setName:[first cellTitle]];
[image lockFocus];
[image writeTIFF:stream];
[image unlockFocus];
NXGetMemoryBuffer(stream, &data, &len, &max);
[pb writeType:NXTIFFPboardType data:data length:len];
NXClose(stream);
NXFreeObjectBuffer(data, len);
}
/* write image path name to pasteboard */
if (pbCopyPath && (path = (char*)[first imagePath])) {
[pb writeType:NXAsciiPboardType data:path length:strlen(path) + 1];
}
/* write custom type */
if (listPaths) [pb writeType:imagePathType data:listPaths length:strlen(listPaths) + 1];
return self;
}
/* copy TIFF from pasteboard */
- copyFromPasteboard
{
id pb, pSave, imageId = (id)nil;
char **types, titleBuff[256], *tiffName = cpNIL;
char *path = (char*)[NXApp lastPath];
NXStream *stream;
/* return if no pasteboard */
if (!(pb = [Pasteboard newName:NXGeneralPboard])) return (id)nil;
/* check for custom type */
for (types = (char**)[pb types]; *types && strcmp(*types,imagePathType); types++);
if (*types) {
char *data;
int len;
if (![pb readType:imagePathType data:&data length:&len]) return (id)nil;
[delegate loadFileString:data :NO :NO];
[pb deallocatePasteboardData:data length:len];
return (id)nil;
}
/* search pasteboard types for a TIFF file */
for (types=(char**)[pb types]; *types && strcmp(*types,(char*)NXTIFFPboardType); types++);
if (!*types) return (id)nil;
/* read data from pasteboard */
if ((stream = [pb readTypeToStream:NXTIFFPboardType]) &&
(imageId = [[NXImage alloc] initFromStream:stream]) &&
([imageId lastRepresentation]) ) {
/* display image */
sprintf(titleBuff, "%s - TIFF", ([imageId name]?[imageId name]:"NXImage"));
[[delegate expandImage] showImage:imageId:(char*)nil title:titleBuff];
/* display save panel */
pSave = [SavePanel new];
[pSave setTitle:"Save TIFF file image"];
[pSave setPrompt:"File:"];
[pSave setRequiredFileType:"tiff"];
[pSave setDirectory:path];
if ([pSave runModalForDirectory:path file:""]) tiffName = (char*)[pSave filename];
/* save to file */
if (tiffName) {
NXSaveToFile(stream, tiffName);
[delegate loadFileString:tiffName :NO :NO];
}
/* free memory */
NXCloseMemory(stream, NX_FREEBUFFER);
} else {
const char *e, *m, *c;
/* show warning/error panel */
e = NXLocalizedString("Error", cpNIL, cpNIL);
m = NXLocalizedString("Unable to read TIFF from PasteBoard.", cpNIL, cpNIL);
c = NXLocalizedString("Continue", cpNIL, cpNIL);
NXRunAlertPanel(e, m, c, cpNIL, cpNIL);
}
/* free image */
[[delegate expandImage] clearImage];
if (imageId) [imageId free];
return self;
}
// -------------------------------------------------------------------------------------
// first responder
#define delKEY 0x7F
/* never relinquish firstResponder */
- resignFirstResponder { return (id)nil; }
/* show preferences (menu connection should point here to allow disabling when necessary) */
- showPreferences:sender { return [NXApp showPreferences:self]; }
/* perform close window functions */
- performClose:sender { return [window performClose:sender]; }
- performMiniaturize:sender { return [window performMiniaturize:sender]; }
/* accept keydown event (check for delete key) */
- keyDown:(NXEvent*)e
{
if (e->data.key.charCode == delKEY) return [self delete:(id)nil];
return [super keyDown:e];
}
/* delete selected cells */
- delete:sender
{
freeLIST(deleteList);
deleteList = [self freeSelectedCells];
[self display];
return self;
}
/* copy previously deleted cells back to imageList */
- undelete:sender
{
int i, c;
id iCell;
if (!deleteList) return self;
for (c = [deleteList count], i = 0; i < c; i++) {
iCell = [deleteList objectAt:i];
if ([self findCellWithImageFilePath:(char*)[iCell imagePath]]) continue;
[iCell setDelegate:delegate];
[self addImageCell:iCell];
[window setDocEdited:YES];
}
[deleteList free];
deleteList = (id)nil;
[self resizeAndDisplay];
return self;
}
/* return true if undeletable */
- (BOOL)undeletable
{
return ([deleteList count])? YES : NO;
}
/* cut selected cells */
- cut:sender
{
[self delete:sender];
if (!deleteList) return self;
if (cutListPaths) { free(cutListPaths); cutListPaths = cpNIL; }
cutListPaths = [self imagePaths:deleteList];
[self copyToPasteboard:deleteList paths:cutListPaths];
return self;
}
/* make a copy of the selected cells */
- copy:sender
{
id dList = [self selectedCellList];
if (!dList) return self;
if (cutListPaths) { free(cutListPaths); cutListPaths = cpNIL; }
cutListPaths = [self imagePaths:dList];
[self copyToPasteboard:dList paths:cutListPaths];
[dList free];
return self;
}
/* copy cut cells back to imageList */
- paste:sender
{
if ([[Pasteboard new] changeCount] != sigPasteboard) [self copyFromPasteboard];
else [delegate loadFileString:cutListPaths :NO :NO];
[self resizeAndDisplay];
return self;
}
/* select all cells */
- selectAll:sender
{
int i;
if (mFlags.radioMode) return self;
i = [self imageListCount];
while(i) [[self imageAt:--i] setState:1];
[self display];
return self;
}
/* sort images by name */
- sortByCellTitle:sender
{
[self shellSort:compareCellTitle];
[self resizeAndDisplay];
return self;
}
/* copy font */
- copyFont:sender
{
pasteFont = [self font];
return self;
}
/* paste font */
- pasteFont:sender
{
if (pasteFont) [self setFont:pasteFont];
return self;
}
// -------------------------------------------------------------------------------------
// first responder: save
- (char*)getPreferenceString:(char*)buff
{
int rows, cols;
[delegate getDisplayedRows:&rows cols:&cols];
sprintf(buff, "%.2f %.0f %.0f %d %d %s %.0f", prefVERSION,
cellSize.width, cellSize.height, cols, rows, [font name], [font pointSize]);
return buff;
}
/* save file names */
- save:sender
{
if ([self imageListCount]) return [delegate save:sender];
return self;
}
- saveAs:sender
{
if ([self imageListCount]) return [delegate saveAs:sender];
return self;
}
/* save to file (called by delegate) */
- saveToFile:(char*)fileName
{
int i, c = [self imageListCount];
char buff[512];
FILE *fNum;
/* get file name to save */
if (!fileName || !c) return (id)nil;
/* open file */
fNum = fopen(fileName, "w");
if (!fNum) return (id)nil;
/* write preferences and path names */
fprintf(fNum, ": %s\n", [self getPreferenceString:buff]);
for (i = 0; i < c; i++) fprintf(fNum, "%s\n", [[self imageAt:i] imagePath]);
/* close file and indicate that window has been saved */
fclose(fNum);
[window setDocEdited:NO];
return self;
}
// -------------------------------------------------------------------------------------
// size
/* set intercell size */
- setIntercell:(const NXSize*)aSize
{
[super setIntercell:aSize];
[self sizeToCells];
return self;
}
/* set image size */
- setCellSize:(const NXSize*)size
{
int i = [self cellCount];
/* update cell size */
[super setCellSize:size];
/* notify cells of size change */
while(i) [[cellList objectAt:--i] setCellSize:size];
[protoCell setCellSize:size];
/* recalc sizes */
[self sizeToCells];
return self;
}
/* readjust matrix to fit cells */
- sizeToCells
{
int i, rows, cols, cnt;
id oldCell;
/* clear selected cells */
[self clearSelectedCell];
/* adjust rows/cols */
cnt = [self imageListCount];
cols = (int)((frame.size.width + intercell.width) / (cellSize.width + intercell.width));
if (cols < 1) cols = 1;
rows = (cnt + cols - 1) / cols;
if (rows < 1) rows = 1;
[self renewRows:rows cols:cols];
/* replace cells (and free old cells) */
for (i = 0; i < cnt; i++) {
oldCell = [cellList replaceObjectAt:i with:[self imageAt:i]];
if (oldCell && ![oldCell image]) [oldCell free];
}
/* now resize matrix */
[super sizeToCells];
return self;
}
/* resize matrix and redisplay */
- resizeAndDisplay
{
[self sizeToCells];
[self display];
return self;
}
/* return cellSize */
- (NXSize*)cellSize
{
return &cellSize;
}
/* return intercellSize */
- (NXSize*)intercell
{
return &intercell;
}
// -------------------------------------------------------------------------------------
// add cell methods
/* called by main thread when cell was added */
- _updateCells
{
[self resizeAndDisplay];
loadingCells = NO;
return self;
}
/* add new image */
- addImage:(const char*)filePath
{
id iCell;
/* see if its already in the list */
if ([self findCellWithImageFilePath:(char*)filePath]) return (id)nil;
/* add and show cell */
iCell = newCELL;
if (![iCell setImageFile:filePath]) { [iCell free]; return (id)nil; }
[iCell setDelegate:delegate];
[self addImageCell:iCell];
if (!loadingCells) {
loadingCells = YES;
[self mainThreadPerform:_updateCells wait:NO];
}
return iCell;
}
/* return current number of image cells */
- (int)imageListCount
{
int cnt;
mutex_lock(loadMutex);
cnt = [imageList count];
mutex_unlock(loadMutex);
return cnt;
}
/* return indexed image cell */
- imageAt:(int)index
{
id iCell;
mutex_lock(loadMutex);
iCell = [imageList objectAt:index];
mutex_unlock(loadMutex);
return iCell;
}
/* add image cell */
- addImageCell:iCell
{
mutex_lock(loadMutex);
[imageList addObject:iCell];
mutex_unlock(loadMutex);
return iCell;
}
// -------------------------------------------------------------------------------------
// cell selection
/* drag image off of window */
- _dragImage:(NXPoint*)mousePt:(NXEvent*)e
{
NXPoint pt = *mousePt;
int row, col;
id cellId;
NXRect rect = { { 0.0, 0.0 }, {48.0, 48.0 } };
/* get cell/row/col */
cellId = [self getRow:&row andCol:&col forPoint:&pt];
if (!cellId || ![cellId imagePath]) return self;
/* message window server for drag */
rect.origin.x = pt.x - rect.size.width / 2.0;
rect.origin.y = pt.y - rect.size.height / 2.0;
[delegate _unregisterWindow];
[self dragFile:[cellId imagePath] fromRect:&rect slideBack:YES event:e];
[delegate _registerWindow];
return self;
}
/* drag cell around matrix */
#define DRAGMASK (NX_MOUSEDRAGGEDMASK | NX_LMOUSEUPMASK)
- _dragCell:(NXPoint*)mousePt:(NXEvent*)e
{
NXPoint pt = *mousePt, lastPt;
int oldMask, row, col;
float minX, maxX, minY, maxY;
NXSize half;
NXRect viewFrame, cellFrame, lastFrame, firstFrame, drawFrame;
id cellId, tempView, cellCache;
/* get/select cell */
[self clearSelectedCell];
cellId = [self getRow:&row andCol:&col forPoint:&pt];
if (!cellId || ![cellId imagePath]) return self;
[self selectCellAt:row:col];
[self display];
/* setup mask for dragging */
oldMask = [window addToEventMask:DRAGMASK];
/* make local image copy of cell */
[self getCellFrame:&cellFrame at:row:col];
viewFrame = cellFrame;
viewFrame.X = viewFrame.Y = 0.0;
half.width = floor(viewFrame.W / 2.0);
half.height = floor(viewFrame.H / 2.0);
cellCache = [[NXImage alloc] initSize:&viewFrame.size];
[cellCache setDataRetained:YES];
[cellCache setFlipped:YES];
[cellCache lockFocus];
tempView = [[[View alloc] initFrame:&viewFrame] setFlipped:YES];
[cellId drawSelf:&viewFrame inView:[[window contentView] addSubview:tempView]];
[tempView free];
[cellCache unlockFocus];
/* set cell drag mode */
[cellId setDragMode:YES];
[self display];
/* get drag boundaries */
[self getCellFrame:&firstFrame at:0:0];
minX = firstFrame.X - half.width;
minY = firstFrame.Y + firstFrame.H - half.height;
[self getCellFrame:&lastFrame at:(numRows-1):(numCols-1)];
maxX = lastFrame.X + lastFrame.W - half.width;
maxY = lastFrame.Y + lastFrame.H + half.height;
/* drag cell image */
[self lockFocus];
lastPt.x = cellFrame.X;
lastPt.y = cellFrame.Y + viewFrame.H;
drawFrame = cellFrame;
[cellCache composite:NX_COPY toPoint:&lastPt];
[window flushWindow];
for (;;) {
NXEvent *ev = [NXApp getNextEvent:DRAGMASK], peek;
while ([NXApp peekNextEvent:DRAGMASK into:&peek]) ev = [NXApp getNextEvent:DRAGMASK];
[self autoscroll:ev];
lastPt = ev->location;
[self convertPoint:&lastPt fromView:(id)nil];
lastPt.x = (numCols > 1)? (lastPt.x - half.width) : 0.0;
lastPt.y += half.height;
if (lastPt.x < minX) lastPt.x = minX;
if (lastPt.x > maxX) lastPt.x = maxX;
if (lastPt.y < minY) lastPt.y = minY;
if (lastPt.y > maxY) lastPt.y = maxY;
if (ev->type == NX_LMOUSEUP) break;
cellFrame.X = lastPt.x;
cellFrame.Y = lastPt.y - viewFrame.H;
NXUnionRect(&cellFrame, &drawFrame);
PSgsave();
NXRectClip(&drawFrame);
[self drawSelf:&drawFrame : 1];
[cellCache composite:NX_COPY toPoint:&lastPt];
[window flushWindow];
PSgrestore();
drawFrame = cellFrame;
}
[self unlockFocus];
[window setEventMask:oldMask];
[cellId setDragMode:NO];
[cellCache free];
/* find insert point */
NXPoint targetPt = lastPt;
targetPt.x += viewFrame.W;
if (numCols > 1) targetPt.y -= half.height;
int nRow = (int)floor((targetPt.y + intercell.height) / (viewFrame.H + intercell.height));
int nCol = (int)floor((targetPt.x + intercell.width ) / (viewFrame.W + intercell.width ));
if (nRow < 0) nRow = 0;
if ((nCol < 0) || (numCols == 1)) nCol = 0;
/* re-arrange cells */
if ((row != nRow) || (col != nCol)) {
/* find end of valid cells */
int oldNdx = (row * numCols) + col;
int newNdx = (nRow * numCols) + nCol;
if (newNdx > (int)[cellList count]) newNdx = [cellList count];
for (;(newNdx > 0) && ![[cellList objectAt:newNdx - 1] image]; newNdx--);
/* move cell */
if (newNdx != (oldNdx + 1)) {
[imageList insertObject:self at:newNdx]; // use 'self' as placeholder
[imageList removeObject:cellId];
[imageList replaceObject:self with:cellId];
[self sizeToCells]; // rebuild/refresh 'cellList'
}
}
/* redisplay matrix */
[self selectCell:cellId];
[self display];
/* return */
return self;
}
#undef DRAGMASK
/* intercept mouse down event */
- mouseDown:(NXEvent*)e
{
/* ignore while images are being loaded into palette */
if ([delegate isLoading]) return self;
/* convert point to view */
NXPoint pt = e->location;
[self convertPoint:&pt fromView:(id)nil];
/* Alternate-Shift */
if ((e->flags) & NX_ALTERNATEMASK) return [self _dragImage:&pt:e];
/* Command-Shift */
if ((e->flags) & NX_COMMANDMASK) return [self _dragCell:&pt:e];
/* return regular mouse-down event */
return [super mouseDown:e];
}
/* return true if there are any selectable cells */
- (BOOL)anySelectableCells
{
return [self imageListCount]? YES : NO;
}
/* find image with name */
- findCellWithImageFilePath:(char*)p
{
int i;
id o, f = (id)nil;
i = [self imageListCount];
while(i) if (!strcmp([(o=[self imageAt:--i]) imagePath], p)) { f = o; break; }
return f;
}
/* return list of selected cells */
- (int)selectedCellCount
{
int c = 0, i;
i = [self imageListCount];
while(i) if ([[self imageAt:--i] isSelected]) c++;
return c;
}
/* return first selected cell found */
- selectedCell
{
int i;
id c, r = (id)nil;
i = [self imageListCount];
while(i) if ([(c = [self imageAt:--i]) isSelected]) r = c;
return r;
}
/* return list of selected cells */
- selectedCellList
{
int i;
id c, list = listALLOC(2);
i = [self imageListCount];
while(i) if ([(c=[self imageAt:--i]) isSelected]) [list addObject:c];
if (![list count]) { [list free]; list = (id)nil; }
return list;
}
/* return string containing file names of all selected cells */
- (char*)selectedCellPaths
{
id list = [self selectedCellList];
char *fullPath = [self imagePaths:list];
[list free];
return fullPath;
}
// -------------------------------------------------------------------------------------
// sort compare methods
/* sort/compare by image name */
- (int)compareCellTitle:image1:image2
{
return strcasecmp([image1 cellTitle], [image2 cellTitle]);
}
// -------------------------------------------------------------------------------------
// sort images
/* indexed from 1 */
#define _COMPARE(I, J) ((int)[self perform:sortCompare with:(I) with:(J)])
#define _ELEM(i) [imageList objectAt:(i) - 1]
#define _setELEM(i, V) [imageList replaceObjectAt:(i) - 1 with:(V)]
/* shell sort: O(N ^ 1.5) */
- shellSort:(SEL)sortCompare
{
int i, j, h, N;
id elem, temp;
/* check for invalid compare method */
if (!sortCompare) return (id)nil;
/* lock imageList */
mutex_lock(loadMutex);
/* determine h: (1 + 3 + 3^2 + 3^3 + 3^4 + ...) [ h = pow(3,floor(log(2*N+1)/log(3))) ]*/
for (N = [imageList count], h = 1; h <= N; h = 3 * h + 1);
/* shell sort */
while (h > 1)
for (h /= 3, i = h + 1; i <= N; i++) {
elem = _ELEM(i);
for (j=i; (j>h) && (_COMPARE((temp=_ELEM(j-h)),elem)>0); j-=h) _setELEM(j, temp);
_setELEM(j, elem);
}
/* unlock imageList */
mutex_unlock(loadMutex);
return self;
}
@end